'''
    @project: 
    @author: Austin Baker
    @created: 11/3/2014
'''
import maya.cmds as cmds


import masdevallia.utility.Dag as Dag
# TOSS
def getMatrixAsList( matrix ):
    listMatrix = []
    for row in range(0,4):
        for cell in range(0,4):
            listMatrix.append( OpenMaya.MScriptUtil.getDoubleArrayItem( matrix[row], cell ) )
    return listMatrix

def printMatrix( matrix ):
    listMatrix = getMatrixAsList( matrix )
    print listMatrix[0], listMatrix[1], listMatrix[2], listMatrix[3]
    print listMatrix[4], listMatrix[5], listMatrix[6], listMatrix[7]
    print listMatrix[8], listMatrix[9], listMatrix[10], listMatrix[11]
    print listMatrix[12], listMatrix[13], listMatrix[14], listMatrix[15]
    print '==========='


'''
============================================================
--->>   IMPORT MODULES
============================================================
'''
import sys
import math
import json
import maya.OpenMaya as OpenMaya
import maya.OpenMayaAnim as OpenMayaAnim


'''
============================================================
--->>   JSON HANDLER CLASSES
============================================================
'''
class SpineJSON( object ):
    '''
    Class for manipulating JSON files.
    '''
    
    def __init__( self, json_filepath=None ):
        '''
        @param json_filepath: String. Full path to a Spine JSON file.
        '''
        # Stores the JSON file path.
        self.filepath = json_filepath
        
        # Stores the bones from the JSON. Dict key is the name of the bone.
        # Dict value is the spine_json_bone class.
        self.bones = {}
        self.bone_build_order = None
        
        # Stores the animation from a JSON.
        self.animation = {}
        
        # Get the file data.
        self.readFile()
        
    def readFile( self ):
        '''
        Reads the JSON file.
        '''
        # Open and load the data.
        json_open = open( self.filepath )
        json_data = json.load( json_open )
        bone_data = json_data['bones'] # This is a list.
        
        # Organize the bones into a build order starting at the root.
        self.bone_build_order = self.buildOrder( bone_data )
        
        for bone_name in self.bone_build_order:
            # Get the bones. This is a list.
            for bone in bone_data:
                if bone['name'] == bone_name:
                    # There is no guarantee that a bone entry in the JSON will
                    # have all the bone attributes.
                    new_bone = SpineBone()
                    
                    for bone_attr in bone.keys():
                        if bone_attr == 'name':
                            # The names from Spine have spaces between words. Maya doesn't
                            # allow this and will automatically replace whitespace with an underscore.
                            new_bone.name = str( bone['name'] ).replace( ' ', '_' )
                    
                        elif bone_attr == 'parent':
                            new_bone.parent = str( bone['parent'] ).replace( ' ', '_' )
                    
                        elif bone_attr == 'length':
                            new_bone.length = float( bone['length'] )
                        
                        elif bone_attr == 'x':
                            new_bone.x = float( bone['x'] )
                    
                        elif bone_attr == 'y':
                            new_bone.y = float( bone['y'] )
                            
                        elif bone_attr == 'scaleX':
                            new_bone.scaleX = float( bone['scaleX'] )
                            
                        elif bone_attr == 'scaleY':
                            new_bone.scaleY = float( bone['scaleY'] )
                            
                        elif bone_attr == 'rotation':
                            new_bone.rotation = float( bone['rotation'] )
                            
                        else:
                            print 'ROGUE BONE ATTR: {0}'.format( bone_attr )
                            
                    # Build a world matrix for this bone.
                    matrix = OpenMaya.MMatrix()
                    bone_mtrans = OpenMaya.MTransformationMatrix( matrix )
                    
                    # Translation.
                    # Defaults.
                    tx = OpenMaya.MDistance( 0.0, OpenMaya.MDistance.kCentimeters )
                    ty = OpenMaya.MDistance( 0.0, OpenMaya.MDistance.kCentimeters )
                    tz = OpenMaya.MDistance( 0.0, OpenMaya.MDistance.kCentimeters )
                    
                    if new_bone.x:
                        tx = OpenMaya.MDistance( new_bone.x, OpenMaya.MDistance.kCentimeters )
                        
                    if new_bone.y:
                        ty = OpenMaya.MDistance( new_bone.y, OpenMaya.MDistance.kCentimeters )
                        
                    tran_vector = OpenMaya.MVector( tx.value(), ty.value(), tz.value() )
                    bone_mtrans.setTranslation( tran_vector, OpenMaya.MSpace.kWorld )
                                        
                    # Rotation.
                    # Defaults
                    rx = math.radians( 0.0 )
                    ry = math.radians( 0.0 )
                    rz = math.radians( 0.0 )
                    
                    if new_bone.rotation:
                        rz = math.radians( new_bone.rotation )

                    rot_util = OpenMaya.MScriptUtil()
                    rot_util.createFromDouble( rx, ry, rz )
                    bone_mtrans.setRotation( rot_util.asDoublePtr(), OpenMaya.MSpace.kWorld )
                    
                    # Scale.
                    # Defaults.
                    sx = 1
                    sy = 1
                    sz = 1
                    
                    if new_bone.scaleX:
                        sx = new_bone.scaleX
                        
                    if new_bone.scaleY:
                        sy = new_bone.scaleY
                    
                    scale_util = OpenMaya.MScriptUtil()
                    scale_util.createFromDouble( sx, sy, sz )
                    bone_mtrans.setScale( scale_util.asDoublePtr(), OpenMaya.MSpace.kWorld )
                    
                    #=================
                    #if new_bone.name == 'body':
                    #print new_bone.name
                    #print '<================='
                    #self.printTransforms( '{0} local'.format( new_bone.name ), bone_mtrans )
                    new_bone.local_matrix = bone_mtrans.asMatrix()
                    #=================                        
                                            
                    if new_bone.parent == None:
                        new_bone.world_matrix = bone_mtrans.asMatrix()
                        new_bone.tran_matrix = bone_mtrans.asMatrix()
                        
                    else:
                        # Get the parent object's matrix.
                        parent_matrix = self.bones[new_bone.parent].tran_matrix
                        #parent_matrix = self.bones[new_bone.parent].local_matrix
                        parent_mtrans = OpenMaya.MTransformationMatrix( parent_matrix )
                        
                        # Set the scale of the parent matrix to that of the bone's.
                        scale_util = OpenMaya.MScriptUtil()
                        scale_util.createFromDouble( sx, sy, sz )
                        parent_mtrans.setScale( scale_util.asDoublePtr(), OpenMaya.MSpace.kWorld )
                        
                        # Get the bone's matrix as a world matrix.
                        final_matrix = bone_mtrans.asMatrix() * parent_mtrans.asMatrix()
                        #final_matrix = new_bone.local_matrix * parent_mtrans.asMatrix()
                        final_mtrans = OpenMaya.MTransformationMatrix( final_matrix )
                        #self.printTransforms('trans matrix', final_mtrans)
                        new_bone.tran_matrix = final_mtrans.asMatrix()
                        
                        
                        # Set the bone's rotation back to the JSON value.
                        rot_matrix = OpenMaya.MMatrix()
                        rot_mtrans = OpenMaya.MTransformationMatrix( rot_matrix )
                        rot_util = OpenMaya.MScriptUtil()
                        rot_util.createFromDouble( rx, ry, rz )
                        #print rx, ry, OpenMaya.MAngle( rz ).asDegrees()
                        rot_mtrans.setRotation( rot_util.asDoublePtr(), OpenMaya.MSpace.kTransform )
                        #self.printTransforms('rot matrix', rot_mtrans)
                        
                        rot_mtrans.setTranslation( final_mtrans.getTranslation( OpenMaya.MSpace.kTransform ), OpenMaya.MSpace.kTransform )
                        
                        #self.printTransforms('rot matrix', rot_mtrans)
              
                        # Set the bone class property.
                        new_bone.world_matrix = rot_mtrans.asMatrix()
                        
                        #==================
                        #if new_bone.name == 'body':
                        #self.printTransforms( '{0} parent world'.format(self.bones[new_bone.parent].name), OpenMaya.MTransformationMatrix( parent_matrix ) )
                        #self.printTransforms( '{0} world'.format( new_bone.name ), rot_mtrans )
                        #print '<================='
                        #==================
                    
                    # Add the bone class instance to the bone property.
                    self.bones[ new_bone.name ] = new_bone
                    
        # Get the animations. This is a dict.
        LOOP_BREAK = 0
        json_anim_data = json_data['animations']
        
        print json_anim_data['CHA_ACT_taDaa_01']['bones']['leftArm']['translate']
        print json_anim_data['CHA_ACT_taDaa_01']['bones']['leftArm']['rotate']
        
        for anim_key in json_anim_data.keys():
            if LOOP_BREAK == 1:
                break
            else:
                new_anim = SpineAnimation()
                
                new_anim.name = anim_key
                
                json_anim_bones_data = json_anim_data[anim_key]['bones']
                for bone_name in json_anim_bones_data.keys():
                    # Convert bone name to a string.
                    json_bone_name = str( bone_name )
                    maya_bone_name = str( bone_name ).replace( ' ', '_' )
                    
                    # Create a bone timeline
                    new_anim.bones[maya_bone_name] = SpineBoneTimeline()
                    new_anim.bones[maya_bone_name].name = maya_bone_name
                    
                    # Loop through the timeline types: translate, scale and rotate.
                    for timeline_type in json_anim_bones_data[json_bone_name].keys():
                        # Get the dictionary of data for the current timeline type.
                        timeline_dict = json_anim_bones_data[json_bone_name][timeline_type]
                        
                        #if maya_bone_name == 'mouth' and new_anim.name == 'CHA_ACT_taDaa_01':
                        #    print timeline_dict
                        
                        # Loop through each key for this timeline item.
                        for timeline_data in timeline_dict:
                            # Loop through each attribute for the timeline item.
                            bone_timeline_list = SpineBoneTimeline.AnimTimeline()
                            
                            for timeline_attr in timeline_data.keys():
                                if timeline_attr == 'curve':
                                    bone_timeline_list.curve = timeline_data[timeline_attr]
                                    
                                elif timeline_attr == 'time':
                                    bone_timeline_list.time = timeline_data[timeline_attr]
                                
                                elif timeline_attr == 'x':
                                    bone_timeline_list.x = timeline_data[timeline_attr]
                                
                                elif timeline_attr == 'y':
                                    bone_timeline_list.y = timeline_data[timeline_attr]
                                
                                elif timeline_attr == 'angle':
                                    bone_timeline_list.angle = timeline_data[timeline_attr]
                                    if bone_name == 'body' and new_anim.name == 'CHA_ACT_taDaa_01':
                                        print '{0}'.format( bone_timeline_list.angle )
                                
                                else:
                                    print '        ROGUE TIMELINE ATTR: {0}'.format( key )
                                    
                            # Add the bone timeline list to the bone timeline object.
                            new_anim.bones[maya_bone_name].timeline[timeline_type].append( bone_timeline_list )
                                    
                # Add the animation to the class property.
                self.animation[anim_key] = new_anim
            
        # Close the JSON file.
        json_open.close()

        
    def buildOrder( self, bone_data ):
        '''
        Puts the bones in the order they need to be built.
        
        @param bone_data: Spine JSON data for bones.
        @return: List of Unicode bone names.
        '''
        bones = []
        for bone in bone_data:
            bones.append( bone['name'] )
            
        build_order = []
        while bones:
            for bone_idx, bone_name in enumerate( bones ):
                for bone in bone_data:
                    if bone_name == bone['name']:
                        try:
                            parent = bone['parent']
                        except:
                            parent = None
                
                        if parent == None:
                            build_order.append( bone_name )
                            bones.pop( bone_idx )
                            
                        elif parent in build_order:
                            build_order.append( bone_name )
                            bones.pop( bone_idx )
                    
        return build_order
    
    
    def printTransforms( self, name, mtrans_matrix ):
        print '    {0}'.format( name )
        #printMatrix( matrix )
        
        trans = mtrans_matrix.getTranslation( OpenMaya.MSpace.kWorld )
        print '        m t: {0},{1},{2}'.format( trans.x, trans.y, trans.z )
        
        rot = mtrans_matrix.eulerRotation()
        print '        m r: {0},{1},{2}'.format( OpenMaya.MAngle( rot.x ).asDegrees(),
                                             OpenMaya.MAngle( rot.y ).asDegrees(),
                                             OpenMaya.MAngle( rot.z ).asDegrees() )
        
        util = OpenMaya.MScriptUtil()
        util.createFromDouble(0.0, 0.0, 0.0)
        ptr = util.asDoublePtr()
        mtrans_matrix.getScale( ptr, OpenMaya.MSpace.kWorld )
        scale = [OpenMaya.MScriptUtil().getDoubleArrayItem( ptr, 0 ),
                 OpenMaya.MScriptUtil().getDoubleArrayItem( ptr, 1 ),
                 OpenMaya.MScriptUtil().getDoubleArrayItem( ptr, 2 )]
        print '        m s: {0},{1},{2}'.format( scale[0], scale[1], scale[2] )
        
        
    def readFileOLD( self ):
        '''
        Reads the JSON file.
        '''
        # Open and load the data.
        json_open = open( self.filepath )
        json_data = json.load( json_open )
        
        # Get the bones. This is a list.
        for bone_data in json_data['bones']:
            # There is no guarantee that a bone entry in the JSON will
            # have all the bone attributes.
            new_bone = SpineBone()
            
            for bone_attr in bone_data.keys():
                if bone_attr == 'name':
                    # The names from Spine have spaces between words. Maya doesn't
                    # allow this and will automatically replace whitespace with an underscore.
                    new_bone.name = str( bone_data['name'] ).replace( ' ', '_' )
            
                elif bone_attr == 'parent':
                    new_bone.parent = str( bone_data['parent'] ).replace( ' ', '_' )
            
                elif bone_attr == 'length':
                    new_bone.length = float( bone_data['length'] )
                
                elif bone_attr == 'x':
                    new_bone.x = float( bone_data['x'] )
            
                elif bone_attr == 'y':
                    new_bone.y = float( bone_data['y'] )
                    
                elif bone_attr == 'scaleX':
                    new_bone.scaleX = float( bone_data['scaleX'] )
                    
                elif bone_attr == 'scaleY':
                    new_bone.scaleY = float( bone_data['scaleY'] )
                    
                elif bone_attr == 'rotation':
                    new_bone.rotation = float( bone_data['rotation'] )
                    
                else:
                    print 'ROGUE BONE ATTR: {0}'.format( bone_attr )            
            
            # Add the bone class instance to the bone property.
            self.bones[ new_bone.name ] = new_bone
                    
        # Get the animations. This is a dict.
        LOOP_BREAK = 0
        json_anim_data = json_data['animations']
        for anim_key in json_anim_data.keys():
            if LOOP_BREAK == 1:
                break
            else:
                new_anim = SpineAnimation()
                
                new_anim.name = anim_key
                
                json_anim_bones_data = json_anim_data[anim_key]['bones']
                for bone_name in json_anim_bones_data.keys():
                    # Convert bone name to a string.
                    json_bone_name = str( bone_name )
                    maya_bone_name = str( bone_name ).replace( ' ', '_' )
                    
                    # Create a bone timeline
                    new_anim.bones[maya_bone_name] = SpineBoneTimeline()
                    new_anim.bones[maya_bone_name].name = maya_bone_name
                    
                    # Loop through the timeline types: translate, scale and rotate.
                    for timeline_type in json_anim_bones_data[json_bone_name].keys():
                        # Get the dictionary of data for the current timeline type.
                        timeline_dict = json_anim_bones_data[json_bone_name][timeline_type]
                        
                        # Loop through each key for this timeline item.
                        for timeline_data in timeline_dict:
                            # Loop through each attribute for the timeline item.
                            bone_timeline_list = SpineBoneTimeline.AnimTimeline()
                            
                            for timeline_attr in timeline_data.keys():
                                if timeline_attr == 'curve':
                                    bone_timeline_list.curve = timeline_data[timeline_attr]
                                    
                                elif timeline_attr == 'time':
                                    bone_timeline_list.time = timeline_data[timeline_attr]
                                
                                elif timeline_attr == 'x':
                                    bone_timeline_list.x = timeline_data[timeline_attr]
                                
                                elif timeline_attr == 'y':
                                    bone_timeline_list.y = timeline_data[timeline_attr]
                                
                                elif timeline_attr == 'angle':
                                    bone_timeline_list.angle = timeline_data[timeline_attr]
                                
                                else:
                                    print '        ROGUE TIMELINE ATTR: {0}'.format( key )
                                    
                            # Add the bone timeline list to the bone timeline object.
                            new_anim.bones[maya_bone_name].timeline[timeline_type].append( bone_timeline_list )
                                    
                # Add the animation to the class property.
                self.animation[anim_key] = new_anim
            
        # Close the JSON file.
        json_open.close()
            

class SpineBone( object ):
    '''
    Stores a Spine bone's data.
    '''
    def __init__( self ):
        self.name = None # string
        self.parent = None # string
        self.length = None # float
        self.x = None # float
        self.y = None # float
        self.scaleX = None # float
        self.scaleY = None # float
        self.rotation = None # float
        self.tran_matrix = None
        self.local_matrix = None
        self.world_matrix = None


class SpineAnimation( object ):
    '''
    Stores a Spine animation's data.
    '''
    def __init__( self ):
        self.name = None
        self.bones = {}


class SpineBoneTimeline( object ):
    '''
    Stores a Spine bone timeline's data.
    '''
    class AnimTimeline():
        '''
        Class for storing a bone's timeline data.
        '''
        def __init__( self ):
            self.time = None # Float, Time in seconds.
            self.curve = None # Can be 'linear', 'stepped' or a bezier as a list [cx1,cy1,cx2,cy2]
            self.x = None # Float. Local space. Relative to setup pose.
            self.y = None # Float. Local space. Relative to setup pose.
            self.angle = None # Float. Local space. Relative to setup pose.
    
    def __init__( self ):
        self.name = None # String. Name of bone.        
        self.timeline = {}
        self.timeline['translate'] = []
        self.timeline['scale'] = []
        self.timeline['rotate'] = []




'''
============================================================
--->>   UTILITY SCRIPTS
============================================================
'''
def getMObject( obj_name ):
    '''
    Converts a Maya object string name into a MObject.
    
    @param obj_name: String. Name of a Maya object.
    @return: MObject or None.
    '''
    try:
        sel_list = OpenMaya.MSelectionList()
        obj = OpenMaya.MObject()
        sel_list.add( obj_name )
        sel_list.getDependNode( 0, obj )
        return obj
    except:
        return None


def getMDagPath( obj_name ):
    '''
    Converts a Maya object string name into a MDagPath.
    
    @param obj_name: String. Name of a Maya object.
    @return: MDagPath or None.
    '''
    try:
        sel_list = OpenMaya.MSelectionList()
        sel_list.add( obj_name )
        dag = OpenMaya.MDagPath()
        component = OpenMaya.MObject()
        sel_list.getDagPath( 0, dag, component )    
        return dag
    except:
        return None
    '''
    except IOError as e:
        print e.errno, e.strerror
    except:
        print sys.exc_info()[0]
    '''
   
    
def getMPlugByName( obj, plug_name ):
    '''
    Get an object's attribute by name.
    
    @param obj: MObject. MObject with the attribute.
    @param plug_name: String. Name of the attribute.
    @return: MPlug or None.
    '''
    dep_fn = OpenMaya.MFnDependencyNode( obj )
    
    try:
        return dep_fn.findPlug( plug_name )
    except:
        return None


def objectExists( obj_name ):
    '''
    Checks if the object exists in the scene.
    
    @param obj_name: String. Name of a Maya object.
    @return: Bool.
    '''
    result = False
    if getMObject( obj_name ) is not None:
        result = True
    return result
    

def translateBy( dag_path, mvector, space ):
    '''
    Relatively change the translation component of the MDagPath.
    
    @param dag_path: MDagPath.
    @param mvector: MVector.
    @param space: MSpace.
    '''
    OpenMaya.MFnTransform( dag_path ).translateBy( mvector, space )
    
    
def rotateBy( dag_path, rotation ):
    '''
    Relatively change the rotation component of the MDagPath.
    
    @param dag_path: MDagPath.
    @param rotation: MEulerRotation or MQuaternion.
    '''
    OpenMaya.MFnTransform( dag_path ).rotateBy( rotation )


'''
============================================================
--->>   BUILDERS
============================================================
'''
def buildSkeleton( json_data, measurement_unit ):
    '''
    Builds a Spine skeleton from the provided JSON file.
    
    @param json_data: SpineJSON class.
    @param measurement_unit: MDistance Unit. kInches, kFeet, kYards, kMiles,
      kMillimeters, kCentimeters, kKilometers, kMeters.
    '''
    dag_mod = OpenMaya.MDagModifier()
    
    for bone in json_data.bone_build_order:
        bone_name = str( bone ).replace( ' ', '_' )

        # Get the bones. This is a list.
        for json_bone_name in json_data.bones.keys():
            if json_bone_name == bone_name:
                print 'BUILD: {0}'.format( bone_name )
                
                json_bone = json_data.bones[json_bone_name]
                
                if json_bone.parent is not None:
                    parent_obj = getMObject( json_bone.parent )
                    bone_obj = dag_mod.createNode( 'joint', parent_obj )
                else:
                    bone_obj = dag_mod.createNode( 'joint' )
                
                dag_mod.doIt()
                
                # Name it.
                dep_fn = OpenMaya.MFnDependencyNode( bone_obj )
                dep_fn.setName( json_bone.name )
                
                # Transform the bone into it's base pose.
                dag_path = getMDagPath( json_bone.name )
                trans_fn = OpenMaya.MFnTransform( dag_path )
                #trans_fn.set( OpenMaya.MTransformationMatrix( json_bone.world_matrix ) )
                bone_mtrans = OpenMaya.MTransformationMatrix( json_bone.world_matrix )
                trans_fn.setTranslation( bone_mtrans.getTranslation( OpenMaya.MSpace.kWorld ), OpenMaya.MSpace.kWorld )
                trans_fn.setRotation( bone_mtrans.rotation() )
                
                util = OpenMaya.MScriptUtil()
                util.createFromDouble(0.0, 0.0, 0.0)
                ptr = util.asDoublePtr()
                bone_mtrans.getScale( ptr, OpenMaya.MSpace.kWorld )
                scale = [OpenMaya.MScriptUtil().getDoubleArrayItem( ptr, 0 ),
                         OpenMaya.MScriptUtil().getDoubleArrayItem( ptr, 1 ),
                         OpenMaya.MScriptUtil().getDoubleArrayItem( ptr, 2 )]
                
                #scale_util = OpenMaya.MScriptUtil()
                #scale_util.createFromDouble( scale[0], scale[1], scale[2] )
                trans_fn.setScale( ptr )
                
                
def buildSkeletonOLD( json_data, measurement_unit ):
    '''
    Builds a Spine skeleton from the provided JSON file.
    
    @param json_data: SpineJSON class.
    @param measurement_unit: MDistance Unit. kInches, kFeet, kYards, kMiles,
      kMillimeters, kCentimeters, kKilometers, kMeters.
    '''
    # Building the skeleton.
    bones_to_build = []
    
    for bone_name in file_data.bones.keys():
        # Get the bone data.  
        bones_to_build.append( bone_name )
        
    # Since the bone positions are in local space the skeleton
    # hierarchy needs to be built from the root up.
    while bones_to_build:
        for bone_idx, bone_name in enumerate( bones_to_build ):
            bone_data = file_data.bones[bone_name]
            # Check if the bone's parent already exists.
            if bone_data.parent == None or objectExists( bone_data.parent ):
                print '    BUILD'
                print '        name: {0}'.format( bone_data.name )
                print '        parent: {0}'.format( bone_data.parent )
                
                # Create the bone.
                dag_mod = OpenMaya.MDagModifier()
                
                if bone_data.parent is not None:
                    parent_obj = getMObject( bone_data.parent )
                    bone_obj = dag_mod.createNode( 'joint', parent_obj )
                else:
                    bone_obj = dag_mod.createNode( 'joint' )
                    
                dag_mod.doIt()
                
                # Name it.
                dep_fn = OpenMaya.MFnDependencyNode( bone_obj )
                dep_fn.setName( bone_data.name )
                
                # Transform the bone into it's base pose.
                dag_path = getMDagPath( bone_data.name )
                
                if bone_data.x is not None or bone_data.y is not None:
                    # Position the bone.    
                    # Spine only deals with X and Y data for positioning.
                    # Leaving Z as 0.
                    # Convert the raw values into MDistance.
                    x = OpenMaya.MDistance( bone_data.x, OpenMaya.MDistance.kCentimeters )
                    y = OpenMaya.MDistance( bone_data.y, OpenMaya.MDistance.kCentimeters )
                    print '        pos: {0}({1}), {2}({3})'.format( bone_data.x, x.asUnits( measurement_unit ), bone_data.y, y.value() )                    
                    
                    # Create a MVector with the values converted to the desired measurement
                    # unit.
                    mvector = OpenMaya.MVector( x.asUnits( measurement_unit ), y.asUnits( measurement_unit ), 0 )
                    translateBy( dag_path, mvector, OpenMaya.MSpace.kObject )
                    
                if bone_data.rotation is not None:
                    # Rotate the bone.
                    print '        rot: {0}'.format( bone_data.rotation )
                    z_radians = math.radians( bone_data.rotation )
                    rotation = OpenMaya.MEulerRotation( 0.0, 0.0, z_radians )
                    rotateBy( dag_path, rotation )
                
                    print '        length: {0}'.format( bone_data.length )
                    print '        scaleX: {0}'.format( bone_data.scaleX )
                    print '        scaleY: {0}'.format( bone_data.scaleY )
                
                
                # Done building bone. Add it to the built list.
                bones_to_build.pop( bone_idx )
            

def resetSkeleton( json_data=None, namespace=None ):
    '''
    Resets a Spine skeleton back to its bind pose.
    
    @param json_data: SpineJSON class.
    @param namespace: String. Namespace of the skeleton. Can be None.
    '''        
    bone_data = json_data.bones
    
    for bone_name in json_data.bone_build_order:
        for bone_key in bone_data.keys():            
            if bone_key == bone_name:
                bone_class = bone_data[bone_key]
                bone_name_full = '{0}{1}'.format( namespace, bone_class.name )
                
                dag_path = getMDagPath( bone_name_full )
                world_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_key].world_matrix )
                trans_fn = OpenMaya.MFnTransform( dag_path )
                trans_fn.setTranslation( world_mtrans.getTranslation( OpenMaya.MSpace.kWorld ), OpenMaya.MSpace.kWorld )
                trans_fn.setRotation( world_mtrans.rotation() )
                
    

def applyAnimationOLD( json_data=None, anim_name=None, measurement_unit=None, namespace=None ):
    '''
    Applies an animation to a Spine JSON skeleton.
    
    @param json_data: SpineJSON class.
    @param anim_name: String. Animation name.
    @param measurement_unit: MDistance Unit. kInches, kFeet, kYards, kMiles,
      kMillimeters, kCentimeters, kKilometers, kMeters.
    @param namespace: String. Namespace of the Spine skeleton. Can be None.
    '''
    # Get the namespace if there is one.
    if namespace is not None:
        namespace = '{0}:'.format( namespace )
    else:
        namespace = ''

    # Reset the skeleton to its bind pose.
    resetSkeleton( json_data=json_data, namespace=namespace )

    # Get the animation timelines.
    anim_data = json_data.animation[anim_name]

    # Loop through each bone in the animation.
    bone_data = json_data.bones
    
    for bone_name in json_data.bone_build_order:
        for bone_key in anim_data.bones.keys():
            if bone_key == bone_name:                
                # Create the animCurve nodes for the animated attributes.
                bone_name_full = '{0}{1}'.format( namespace, bone_name )
                
                print 'Applying animation to... {0}'.format( bone_name )
                
                dag_path = getMDagPath( bone_name_full )
                
                '''                
                anim_curve_fn = OpenMayaAnim.MFnAnimCurve()
                
                x_tran_plug = getMPlugByName( dag_path.node(), 'translateX' )
                x_tran_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( x_tran_plug, x_tran_array ):
                    x_tran_anim_node = anim_curve_fn.create( x_tran_plug )
                else:
                    x_tran_anim_node = x_tran_array[0]            
                
                y_tran_plug = getMPlugByName( dag_path.node(), 'translateY' )
                y_tran_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( y_tran_plug, y_tran_array ):
                    y_tran_anim_node = anim_curve_fn.create( y_tran_plug )
                else:
                    y_tran_anim_node = y_tran_array[0]
                
                z_rot_plug = getMPlugByName( dag_path.node(), 'rotateZ' )
                z_rot_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( z_rot_plug, z_rot_array ):
                    z_rot_anim_node = anim_curve_fn.create( z_rot_plug )
                else:
                    z_rot_anim_node = z_rot_array[0]
                '''
                # Loop through each bone timeline.
                for timeline_type in anim_data.bones[bone_name].timeline.keys():
                    print '    Applying {0}'.format( timeline_type )
                    # Get the list of AnimTimeline associated with this timeline type.
                    timeline_list = anim_data.bones[bone_name].timeline[timeline_type]
                    
                    # The list is in order from first key to last.
                    for keyframe in timeline_list:
                        # Time is in seconds. Convert to uiUnit used in Maya.
                        frame = math.ceil( (keyframe.time * 30) )
                        mtime = OpenMaya.MTime( frame, OpenMaya.MTime.uiUnit() )
                        print '        Keyframe {0}'.format( mtime.value() )
                        
                        if keyframe.x is not None or keyframe.y is not None:
                            '''
                            
                            # ADD JUNK HERE FOR DEFAULT ASSUMED VALUES IF THE STORED IS NONE.
                            
                            
                            # Translate the bone.
                            print '            {0}, {1}'.format( keyframe.x, keyframe.y )
                            x = OpenMaya.MDistance( keyframe.x, OpenMaya.MDistance.kCentimeters )
                            y = OpenMaya.MDistance( keyframe.y, OpenMaya.MDistance.kCentimeters )
                            
                            # The values stored in the Spine JSON are relative to the bone's parent.
                            # A quick way to get the correct values to key is to translate the object,
                            # get the attribute's values and key them.
                            mvector = OpenMaya.MVector( x.asUnits( measurement_unit ), y.asUnits( measurement_unit ), 0.0 )
                            
                            if dag_path.partialPathName() == 'target:body':
                                print mvector.x, mvector.y, mvector.z
                            
                            
                            bone_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_name].local_matrix )
                            #json_data.printTransforms( 'pre-move', bone_mtrans )
                            bone_mtrans.addTranslation( mvector, OpenMaya.MSpace.kObject )
                            #json_data.printTransforms( 'post-move', bone_mtrans )
                            OpenMaya.MFnTransform( dag_path ).setTranslation( bone_mtrans.getTranslation( OpenMaya.MSpace.kObject ), OpenMaya.MSpace.kObject )
                            
                            
                            #translateBy( dag_path, mvector, OpenMaya.MSpace.kObject )
                            
                            # Do the keying.                    
                            print '            tran x: {0}, ({1})'.format( x_tran_plug.asMDistance().asUnits( measurement_unit ), keyframe.x )
                            anim_curve_fn.setObject( x_tran_anim_node )
                            #anim_curve_fn.addKeyframe( mtime, x.asUnits( measurement_unit ) )
                            anim_curve_fn.addKeyframe( mtime, x_tran_plug.asMDistance().asUnits( measurement_unit ) )
                            
                            print '            tran y: {0}, ({1})'.format( y_tran_plug.asMDistance().asUnits( measurement_unit ), keyframe.y )
                            anim_curve_fn.setObject( y_tran_anim_node )
                            #anim_curve_fn.addKeyframe( mtime, y.asUnits( measurement_unit ) )
                            anim_curve_fn.addKeyframe( mtime, y_tran_plug.asMDistance().asUnits( measurement_unit ) )
                            '''
                            pass
                        if keyframe.angle is not None:
                            # Rotate the bone.
                            z_radians = math.radians( keyframe.angle )
                            angle = OpenMaya.MEulerRotation( 0.0, 0.0, z_radians )
                            
                            bone_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_name].local_matrix )
                            bone_mtrans.rotateBy( angle, OpenMaya.MSpace.kWorld )
                            OpenMaya.MFnTransform( dag_path ).rotateBy( bone_mtrans.eulerRotation() )
                                           
                            #rotateBy( dag_path, angle )
                            
                            #print '            rot: {0}'.format( z_rot_plug.asMAngle().asUnits( OpenMaya.MAngle.kDegrees ) )
                            
                            #anim_curve_fn.setObject( z_rot_anim_node )
                            #anim_curve_fn.addKeyframe( mtime, angle.z )
                            #anim_curve_fn.addKeyframe( mtime, z_rot_plug.asMAngle().asUnits( OpenMaya.MAngle.kDegrees ) )
                            
                            mel_command = 'setKeyframe'
                            mel_command += ' -time {0}'.format( mtime.value() )
                            mel_command += ' -attribute rotateZ'
                            mel_command += ' {0};'.format( bone_name_full )
                            OpenMaya.MGlobal.executeCommand( mel_command )
                            print mel_command
                            
                        # Set the keyframe.
                        #OpenMayaAnim.MFnAnimCurve( dag_path.node() )
                            
                        if keyframe.curve is not None:
                            # Apply the curve type to the keyframe.
                            pass
                
          
def applyAnimationOLD2( json_data=None, anim_name=None, measurement_unit=None, namespace=None ):
    '''
    Applies an animation to a Spine JSON skeleton.
    
    @param json_data: SpineJSON class.
    @param anim_name: String. Animation name.
    @param measurement_unit: MDistance Unit. kInches, kFeet, kYards, kMiles,
      kMillimeters, kCentimeters, kKilometers, kMeters.
    @param namespace: String. Namespace of the Spine skeleton. Can be None.
    '''
    # Get the namespace if there is one.
    if namespace is not None:
        namespace = '{0}:'.format( namespace )
    else:
        namespace = ''

    # Reset the skeleton to its bind pose.
    resetSkeleton( json_data=json_data, namespace=namespace )

    # Get the animation timelines.
    anim_data = json_data.animation[anim_name]

    # Loop through each bone in the animation.
    bone_data = json_data.bones
    
    for bone_name in json_data.bone_build_order:
        for bone_key in anim_data.bones.keys():
            if bone_key == bone_name and bone_key == 'leftArm':
                # Create the animCurve nodes for the animated attributes.
                bone_name_full = '{0}{1}'.format( namespace, bone_name )
                
                print 'Applying animation to... {0}'.format( bone_name )
                
                dag_path = getMDagPath( bone_name_full )
                
                # Prep for adding animations.
                anim_curve_fn = OpenMayaAnim.MFnAnimCurve()
    
                x_tran_plug = getMPlugByName( dag_path.node(), 'translateX' )
                x_tran_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( x_tran_plug, x_tran_array ):
                    x_tran_anim_node = anim_curve_fn.create( x_tran_plug )
                else:
                    x_tran_anim_node = x_tran_array[0]            
                
                y_tran_plug = getMPlugByName( dag_path.node(), 'translateY' )
                y_tran_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( y_tran_plug, y_tran_array ):
                    y_tran_anim_node = anim_curve_fn.create( y_tran_plug )
                else:
                    y_tran_anim_node = y_tran_array[0]
                
                z_rot_plug = getMPlugByName( dag_path.node(), 'rotateZ' )
                z_rot_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( z_rot_plug, z_rot_array ):
                    z_rot_anim_node = anim_curve_fn.create( z_rot_plug )
                else:
                    z_rot_anim_node = z_rot_array[0]
                
                # Loop through each bone timeline.
                for timeline_type in anim_data.bones[bone_name].timeline.keys():
                    print '    Applying {0}'.format( timeline_type )
                    
                    # Get the list of AnimTimeline associated with this timeline type.
                    timeline_list = anim_data.bones[bone_name].timeline[timeline_type]
                    
                    # The list is in order from first key to last.
                    for keyframe_idx, keyframe in enumerate( timeline_list ):
                        print '        {0}'.format( keyframe_idx )
                        previous_keyframe = timeline_list[keyframe_idx]
                        
                        if keyframe_idx != 0:
                            previous_keyframe = timeline_list[keyframe_idx-1]
                        
                        # Time is in seconds. Convert to uiUnit used in Maya.
                        frame = math.ceil( (keyframe.time * 30) )
                        mtime = OpenMaya.MTime( frame, OpenMaya.MTime.uiUnit() )
                        print '        Keyframe {0}'.format( mtime.value() )
                            
                        if timeline_type == 'translate':                            
                            if keyframe.x is not None or keyframe.y is not None:
                                #print '            {0}, {1}'.format( keyframe.x, keyframe.y )
                                # Translate the bone.
                                x = OpenMaya.MDistance( keyframe.x, OpenMaya.MDistance.kCentimeters )
                                y = OpenMaya.MDistance( keyframe.y, OpenMaya.MDistance.kCentimeters )
                                mvector = OpenMaya.MVector( x.asUnits( measurement_unit ), y.asUnits( measurement_unit ), 0.0 )
                                #print '            {0},{1},{2}'.format( mvector.x, mvector.y, mvector.z )
                                
                                bone_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_name].local_matrix )
                                
                                                            #trans = bone_mtrans.getTranslation( OpenMaya.MSpace.kTransform )
                                #print '            m t: {0},{1},{2}'.format( trans.x, trans.y, trans.z )
                                                            
                                bone_mtrans.addTranslation( mvector, OpenMaya.MSpace.kTransform )
                                
                                #trans = bone_mtrans.getTranslation( OpenMaya.MSpace.kObject )
                                #print '            m t: {0},{1},{2}'.format( trans.x, trans.y, trans.z )
                                
                                #trans = bone_mtrans.getTranslation( OpenMaya.MSpace.kWorld )
                                #print '            m t: {0},{1},{2}'.format( trans.x, trans.y, trans.z )
                                
                                #'''
                                final_x = bone_mtrans.getTranslation( OpenMaya.MSpace.kTransform ).x
                                final_y = bone_mtrans.getTranslation( OpenMaya.MSpace.kTransform ).y
                                
                                #print '            {0}, {1}'.format( final_x, final_y )
                                    
                                tangent_in = getKeyTangent( previous_keyframe.curve )
                                tangent_out = getKeyTangent( keyframe.curve )
                                
                                anim_curve_fn.setObject( x_tran_anim_node )
                                anim_curve_fn.addKeyframe( mtime, final_x, tangent_in, tangent_out )
                                
                                anim_curve_fn.setObject( y_tran_anim_node )
                                anim_curve_fn.addKeyframe( mtime, final_y, tangent_in, tangent_out )
                                                                
                                if tangent_in == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, previous_keyframe.curve[2], previous_keyframe.curve[3], True )
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, True )
                                    
                                if tangent_out == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, keyframe.curve[0], keyframe.curve[1], False )
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, True )
                                #'''
                                
                                '''
                                trans_fn = OpenMaya.MFnTransform( dag_path )
                                trans_fn.set( bone_mtrans )
                                
                                
                                
                                mel_command = 'setKeyframe'
                                mel_command += ' -time {0}'.format( mtime.value() )
                                mel_command += ' -attribute translateX'
                                mel_command += ' -attribute translateY'
                                mel_command += ' {0};'.format( bone_name_full )
                                OpenMaya.MGlobal.executeCommand( mel_command )
                                
                                if mtime.value() == 45.0:
                                    pass#return
                                '''
                        elif timeline_type == 'rotate':
                            if keyframe.angle is not None:
                                print '            {0}'.format( keyframe.angle )
                                bone_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_name].local_matrix )
                                
                                rvec = OpenMaya.MVector( 0.0, 0.0, OpenMaya.MAngle( keyframe.angle, OpenMaya.MAngle.kDegrees ).asRadians() )
                                euler = OpenMaya.MEulerRotation( rvec )
                                
                                print '            {0}'.format( OpenMaya.MAngle( euler.z, OpenMaya.MAngle.kRadians ).asDegrees() )
                                
                                bone_mtrans.rotateBy( euler, OpenMaya.MSpace.kWorld )
                                
                                rot = bone_mtrans.eulerRotation()
                                final_z = OpenMaya.MAngle( rot.z, OpenMaya.MAngle.kRadians )
                                
                                tangent_in = getKeyTangent( previous_keyframe.curve )
                                tangent_out = getKeyTangent( keyframe.curve )
                                #print previous_keyframe.curve
                                #print keyframe.curve
                                
                                anim_curve_fn.setObject( z_rot_anim_node )
                                anim_curve_fn.addKeyframe( mtime, final_z.asRadians(), tangent_in, tangent_out )
                                
                                # Filter the curve to remove gimbal lock.
                                node_name = OpenMaya.MFnDependencyNode( z_rot_anim_node ).name()
                                mel_command = 'filterCurve {0}'.format( node_name )
                                OpenMaya.MGlobal.executeCommand( mel_command )
                                
                                if tangent_in == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, previous_keyframe.curve[2], previous_keyframe.curve[3], True )
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, True )
                                    
                                if tangent_out == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, keyframe.curve[0], keyframe.curve[1], False )
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, True )
                                
                                #trans_fn = OpenMaya.MFnTransform( dag_path )
                                #trans_fn.set( bone_mtrans )
                                
                                #mel_command = 'setKeyframe'
                                #mel_command += ' -time {0}'.format( mtime.value() )
                                #mel_command += ' -attribute rotateZ'
                                #mel_command += ' {0};'.format( bone_name_full )
                                #OpenMaya.MGlobal.executeCommand( mel_command )
                        elif timeline_type == 'scale':
                            pass
                            
                        






def applyAnimation( json_data=None, anim_name=None, measurement_unit=None, namespace=None ):
    '''
    Applies an animation to a Spine JSON skeleton.
    
    @param json_data: SpineJSON class.
    @param anim_name: String. Animation name.
    @param measurement_unit: MDistance Unit. kInches, kFeet, kYards, kMiles,
      kMillimeters, kCentimeters, kKilometers, kMeters.
    @param namespace: String. Namespace of the Spine skeleton. Can be None.
    '''
    # Get the namespace if there is one.
    if namespace is not None:
        namespace = '{0}:'.format( namespace )
    else:
        namespace = ''

    # Reset the skeleton to its bind pose.
    resetSkeleton( json_data=json_data, namespace=namespace )

    # Get the animation timelines.
    anim_data = json_data.animation[anim_name]

    # Loop through each bone in the animation.
    bone_data = json_data.bones
    
    for bone_name in json_data.bone_build_order:
        for bone_key in anim_data.bones.keys():
            if bone_key == bone_name:# and bone_key == 'leftArm':
                # Create the animCurve nodes for the animated attributes.
                bone_name_full = '{0}{1}'.format( namespace, bone_name )
                
                print 'Applying animation to... {0}'.format( bone_name )
                
                dag_path = getMDagPath( bone_name_full )
                
                # Prep for adding animations.
                anim_curve_fn = OpenMayaAnim.MFnAnimCurve()
    
                x_tran_plug = getMPlugByName( dag_path.node(), 'translateX' )
                x_tran_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( x_tran_plug, x_tran_array ):
                    x_tran_anim_node = anim_curve_fn.create( x_tran_plug )
                else:
                    x_tran_anim_node = x_tran_array[0]            
                
                y_tran_plug = getMPlugByName( dag_path.node(), 'translateY' )
                y_tran_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( y_tran_plug, y_tran_array ):
                    y_tran_anim_node = anim_curve_fn.create( y_tran_plug )
                else:
                    y_tran_anim_node = y_tran_array[0]
                
                z_rot_plug = getMPlugByName( dag_path.node(), 'rotateZ' )
                z_rot_array = OpenMaya.MObjectArray()
                if not OpenMayaAnim.MAnimUtil.findAnimation( z_rot_plug, z_rot_array ):
                    z_rot_anim_node = anim_curve_fn.create( z_rot_plug )
                else:
                    z_rot_anim_node = z_rot_array[0]
                
                # Loop through each bone timeline.
                for timeline_type in anim_data.bones[bone_name].timeline.keys():
                    print '    Applying {0}'.format( timeline_type )
                    
                    # Get the list of AnimTimeline associated with this timeline type.
                    timeline_list = anim_data.bones[bone_name].timeline[timeline_type]
                    
                    # The list is in order from first key to last.
                    # First create all the keys.
                    for keyframe_idx, keyframe in enumerate( timeline_list ):
                        print '        key index: {0}'.format( keyframe_idx )
                        previous_keyframe = timeline_list[keyframe_idx]
                        
                        if keyframe_idx != 0:
                            previous_keyframe = timeline_list[keyframe_idx-1]
                        
                        # Time is in seconds. Convert to uiUnit used in Maya.
                        frame = math.ceil( (keyframe.time * 30) )
                        mtime = OpenMaya.MTime( frame, OpenMaya.MTime.uiUnit() )
                        print '        Keyframe {0}'.format( mtime.value() )
                        
                        # Convert the Spine tangent into a Maya tangent.
                        tangent_in = getKeyTangent( previous_keyframe.curve )
                        tangent_out = getKeyTangent( keyframe.curve )
                            
                        if timeline_type == 'translate':
                            if keyframe.x is not None or keyframe.y is not None:
                                print '            translate'
                                # Get the new transform of the joint for this frame.
                                x = OpenMaya.MDistance( keyframe.x, OpenMaya.MDistance.kCentimeters )
                                y = OpenMaya.MDistance( keyframe.y, OpenMaya.MDistance.kCentimeters )
                                mvector = OpenMaya.MVector( x.asUnits( measurement_unit ), y.asUnits( measurement_unit ), 0.0 )
                                bone_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_name].local_matrix )
                                bone_mtrans.addTranslation( mvector, OpenMaya.MSpace.kTransform )
                                final_x = bone_mtrans.getTranslation( OpenMaya.MSpace.kTransform ).x
                                final_y = bone_mtrans.getTranslation( OpenMaya.MSpace.kTransform ).y
                                
                                # Add the X keyframe to the animCurve.
                                print '                t-in: {0}'.format(tangent_in)
                                print '                t-out: {0}'.format(tangent_out)
                                anim_curve_fn.setObject( x_tran_anim_node )
                                anim_curve_fn.setIsWeighted( True )
                                anim_curve_fn.addKeyframe( mtime, final_x, tangent_in, tangent_out )
                                
                                # Add the Y keyframe to the animCurve.
                                anim_curve_fn.setObject( y_tran_anim_node )
                                anim_curve_fn.setIsWeighted( True )
                                anim_curve_fn.addKeyframe( mtime, final_y, tangent_in, tangent_out )
                                                                       
                        elif timeline_type == 'rotate':
                            if keyframe.angle is not None:
                                print '            rotate'
                                # Get the new transform of the joint for this frame.
                                bone_mtrans = OpenMaya.MTransformationMatrix( bone_data[bone_name].local_matrix )
                                rvec = OpenMaya.MVector( 0.0, 0.0, OpenMaya.MAngle( keyframe.angle, OpenMaya.MAngle.kDegrees ).asRadians() )
                                euler = OpenMaya.MEulerRotation( rvec )
                                bone_mtrans.rotateBy( euler, OpenMaya.MSpace.kWorld )
                                rot = bone_mtrans.eulerRotation()
                                final_z = OpenMaya.MAngle( rot.z, OpenMaya.MAngle.kRadians )
                                                                
                                # Add the Z keyframe to the animCurve.
                                print '                t-in: {0}'.format( tangent_in )
                                print '                t-out: {0}'.format( tangent_out )
                                anim_curve_fn.setObject( z_rot_anim_node )
                                anim_curve_fn.addKeyframe( mtime, final_z.asRadians(), tangent_in, tangent_out )
                                
                                # Filter the curve to remove gimbal lock.
                                node_name = OpenMaya.MFnDependencyNode( z_rot_anim_node ).name()
                                mel_command = 'filterCurve {0}'.format( node_name )
                                OpenMaya.MGlobal.executeCommand( mel_command )
                                
                        elif timeline_type == 'scale':
                            pass

                    # Now handle the key tangents. We do this separate because it relies on
                    # looking at keyframes before and after the key being adjusted.
                    for keyframe_idx, keyframe in enumerate( timeline_list ):
                        # Time is in seconds. Convert to uiUnit used in Maya.
                        frame = math.ceil( (keyframe.time * 30) )
                        mtime = OpenMaya.MTime( frame, OpenMaya.MTime.uiUnit() )
                        
                        # Convert the Spine tangent into a Maya tangent.
                        tangent_in = getKeyTangent( previous_keyframe.curve )
                        tangent_out = getKeyTangent( keyframe.curve )
                             
                        if timeline_type == 'translate':                    
                            # If the Spine tangents are bezier then do the conversion and setup here. 
                            if tangent_in == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                if keyframe_idx != 0:
                                    print '                    t-in'
                                    # In tangent to the key (left side of key). This
                                    # value is taken from the previous key's entry.
                                                                                                                
                                    # Get the values for this key and the next.
                                    cur_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx ), OpenMaya.MAngle.kRadians )
                                    prev_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx - 1 ), OpenMaya.MAngle.kRadians )
                                    diff_value = abs( prev_key_value.asDegrees() - cur_key_value.asDegrees() )
                                    
                                    # Get the percentage of the difference.
                                    diff_perc = diff_value * previous_keyframe.curve[3]
                                    
                                    # Create the tangent angle.
                                    angle = OpenMaya.MAngle( diff_perc, OpenMaya.MAngle.kDegrees )

                                    # Get the MTimes for this key and the next.
                                    cur_key_time = anim_curve_fn.time( keyframe_idx )
                                    prev_key_time = anim_curve_fn.time( keyframe_idx - 1 )
                                    
                                    # Get the difference in times.
                                    diff_time = abs( (prev_key_time.value() - cur_key_time.value()) + 1 )
                                    
                                    # Create the tangent weight.
                                    x = (diff_time * previous_keyframe.curve[2])
                                    weight = math.sqrt( angle.asRadians()*angle.asRadians() + x*x )
                                    
                                    # Set the tangent.
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setWeightsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, angle, weight, True )
                                                                            
                            if tangent_out == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                if keyframe_idx != len(timeline_list)-1:
                                    print '                    t-out'
                                    # Out tangent to the key (right side of the key).
                                    # This value is taken from this key's entry.
                                    
                                    # Get the values for this key and the next.
                                    cur_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx ), OpenMaya.MAngle.kRadians )
                                    next_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx + 1 ), OpenMaya.MAngle.kRadians )
                                    diff_value = abs( next_key_value.asDegrees() - cur_key_value.asDegrees() )
                                    
                                    # Get the percentage of the difference.
                                    diff_perc = diff_value * keyframe.curve[0]
                                    
                                    # Create the tangent angle.
                                    angle = OpenMaya.MAngle( diff_perc, OpenMaya.MAngle.kDegrees )

                                    # Get the MTimes for this key and the next.
                                    cur_key_time = anim_curve_fn.time( keyframe_idx )
                                    next_key_time = anim_curve_fn.time( keyframe_idx + 1 )
                                    
                                    # Get the difference in times.
                                    diff_time = abs( (next_key_time.value() - cur_key_time.value()) + 1 )
                                    
                                    # Create the tangent weight.
                                    x = (diff_time * keyframe.curve[1])
                                    weight = math.sqrt( angle.asRadians()*angle.asRadians() + x*x )
                                    
                                    # Set the tangent.
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setWeightsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, angle, weight, False )

                        elif timeline_type == 'rotate':                                
                            if tangent_in == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                if keyframe_idx != 0:
                                    print '                    t-in'
                                    # In tangent to the key (left side of key). This
                                    # value is taken from the previous key's entry.
                                                                            
                                    # Get the values for this key and the next.
                                    cur_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx ), OpenMaya.MAngle.kRadians )
                                    prev_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx - 1 ), OpenMaya.MAngle.kRadians )
                                    diff_value = abs( prev_key_value.asDegrees() - cur_key_value.asDegrees() )
                                    
                                    # Get the percentage of the difference.
                                    diff_perc = diff_value * previous_keyframe.curve[3]
                                    
                                    # Create the tangent angle.
                                    angle = OpenMaya.MAngle( diff_perc, OpenMaya.MAngle.kDegrees )

                                    # Get the MTimes for this key and the next.
                                    cur_key_time = anim_curve_fn.time( keyframe_idx )
                                    prev_key_time = anim_curve_fn.time( keyframe_idx - 1 )
                                    
                                    # Get the difference in times.
                                    diff_time = abs( (prev_key_time.value() - cur_key_time.value()) + 1 )
                                    
                                    # Create the tangent weight.
                                    x = (diff_time * previous_keyframe.curve[2])
                                    weight = math.sqrt( angle.asRadians()*angle.asRadians() + x*x )
                                    
                                    # Set the tangent.
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setWeightsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, angle, weight, True )
                                                                            
                            if tangent_out == OpenMayaAnim.MFnAnimCurve.kTangentSmooth:
                                if keyframe_idx != len(timeline_list)-1:
                                    print '                t-out'
                                    # Out tangent to the key (right side of the key).
                                    # This value is taken from this key's entry.
                                    
                                    # Get the values for this key and the next.
                                    cur_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx ), OpenMaya.MAngle.kRadians )
                                    next_key_value = OpenMaya.MAngle( anim_curve_fn.value( keyframe_idx + 1 ), OpenMaya.MAngle.kRadians )
                                    diff_value = abs( next_key_value.asDegrees() - cur_key_value.asDegrees() )
                                    
                                    print '                    diff_value: {0}'.format( diff_value )
                                    
                                    # Get the percentage of the difference.
                                    diff_perc = diff_value * keyframe.curve[0]
                                    
                                    # Create the tangent angle.
                                    angle = OpenMaya.MAngle( diff_perc, OpenMaya.MAngle.kDegrees )

                                    # Get the MTimes for this key and the next.
                                    cur_key_time = anim_curve_fn.time( keyframe_idx )
                                    next_key_time = anim_curve_fn.time( keyframe_idx + 1 )
                                    
                                    # Get the difference in times.
                                    diff_time = abs( (next_key_time.value() - cur_key_time.value()) + 1 )
                                    
                                    # Create the tangent weight.
                                    x = (diff_time * keyframe.curve[1])
                                    weight = math.sqrt( angle.asRadians()*angle.asRadians() + x*x )
                                    
                                    # Set the tangent.
                                    anim_curve_fn.setTangentsLocked( keyframe_idx, False )
                                    anim_curve_fn.setWeightsLocked( keyframe_idx, False )
                                    anim_curve_fn.setTangent( keyframe_idx, angle, weight, False )
                        elif timeline_type == 'scale':
                            pass




def getKeyTangent( curve_type ):
    '''
    '''
    if curve_type is None or curve_type == 'linear':
        return OpenMayaAnim.MFnAnimCurve.kTangentLinear
    elif curve_type == 'stepped':
        return OpenMayaAnim.MFnAnimCurve.kTangentStep
    elif type(curve_type) == list:
        return OpenMayaAnim.MFnAnimCurve.kTangentSmooth
                        

                            
                            
'''
============================================================
--->>   TESTNG TESTING TESTING
============================================================
'''   

json_filepath = 'E:\\Carey Project\\Panda\\PandaHiRes\\skeleton.json'
file_data = SpineJSON( json_filepath=json_filepath )

# Build the skeleton.
#buildSkeleton( file_data, OpenMaya.MDistance.kCentimeters )
 

           
anim_name = 'CHA_ACT_taDaa_01'
measurement_unit = OpenMaya.MDistance.kCentimeters
namespace = 'target'
applyAnimation( json_data=file_data, anim_name=anim_name, measurement_unit=measurement_unit, namespace=namespace )

#resetSkeleton( json_data=file_data, namespace=namespace )

'''
[{u'angle': 0, u'time': 0},
{u'curve': [0.25, 0, 0.75, 1], u'angle': -346.73, u'time': 0.1666},
{u'curve': [0.25, 0, 0.75, 1], u'angle': -264.28, u'time': 0.6666},
{u'curve': [0.32, 0.28, 0.757, 1], u'angle': -315.51, u'time': 0.8},
{u'curve': u'stepped', u'angle': -299.67, u'time': 0.9666},
{u'curve': [0.25, 0, 0.75, 1], u'angle': -299.67, u'time': 1.3333},
{u'curve': [0.25, 0, 0.75, 1], u'angle': -279.18, u'time': 1.5666},
{u'curve': u'stepped', u'angle': 0, u'time': 1.9},
{u'angle': 0, u'time': 2.1666}]
'''

'''
x = 0.32
y = 0.28
deg = math.atan2( y, x )
print deg * (180/math.pi)

x = 0.757
y = 1
deg2 = math.atan2( y, x )
print deg2 * (180/math.pi)


obj_name = 'target:leftArm'
obj = Dag.getMObject( obj_name )
dag_path = Dag.getDagPath( obj )

anim_curve_fn = OpenMayaAnim.MFnAnimCurve()

z_rot_plug = getMPlugByName( dag_path.node(), 'rotateZ' )
z_rot_array = OpenMaya.MObjectArray()
if not OpenMayaAnim.MAnimUtil.findAnimation( z_rot_plug, z_rot_array ):
    z_rot_anim_node = anim_curve_fn.create( z_rot_plug )
else:
    z_rot_anim_node = z_rot_array[0]

anim_curve_fn.setObject( z_rot_anim_node )
for keyframe_idx in xrange( anim_curve_fn.numKeys() ):
    print keyframe_idx
    if keyframe_idx == 2:
        # Get the number
        anim_curve_fn.setTangentsLocked( keyframe_idx, False )
        
        # Tangents from Spine.
        # x is a % of the time between 2 keys. 0-1
        # y is a % difference between 2 key's values. 0-1
        
        # Weighted Tangents in Maya.
        # x is a % of the distance between two keys.
        # in and out tangents both start at 0. so going left to right
        # the out tangent would be 0.25 and so will the in tangent of the
        # next key (instead of 0.75 like it is in Spine which reads left to right).
        # In Maya the the out tangents read left to right and the in tangents left to right.
        # Y 
        
        
        
        
        # angle = atan(y/x)
        # weight = x/(3*cos(angle))
        angle = OpenMaya.MAngle( math.atan2(0.0, 0.25) )
        print 'angle: {0}'.format( angle.asDegrees() )
        #weight = math.sqrt( (0.25 ** 2)+(0.0 ** 2) )
        weight = 0.25 / ( 3 * math.cos(angle.asDegrees()) )
        print 'weight: {0}'.format( weight )
                
        anim_curve_fn.setTangent( keyframe_idx, angle, weight, True )        
        anim_curve_fn.setTangentsLocked( keyframe_idx, True )
'''






'''
2.22044604925e-16 1.0 0.0 0.0
-1.0 2.22044604925e-16 0.0 0.0
0.0 0.0 1.0 0.0
2.42 288.4 0.0 1.0

2.22044604925e-16 1.0 0.0 0.0
-1.0 2.22044604925e-16 0.0 0.0
0.0 0.0 1.0 0.0
8.97 203.76 0.0 1.0

'''


''' 
self.time = None # Float, Time in seconds.
self.curve = None # Can be 'linear', 'stepped' or a bezier as a list [cx1,cy1,cx2,cy2]
self.x = None # Float. Local space. Relative to setup pose.
self.y = None # Float. Local space. Relative to setup pose.
self.angle = None # Float. Local space. Relative to setup pose.
'''


'''
x = 2.867
y = 1.487
rot_z = 22.105

#base_matrix = APIU.getMatrix( APIU.getMObject('pCone1'), 'worldMatrix' )
base_matrix = APIU.getMatrix( APIU.getMObject('pCone1'), 'matrix' )
target_matrix = APIU.getMatrix( APIU.getMObject('pCone2'), 'worldMatrix' )

obj_name = 'pCone3'
obj = APIU.getMObject( obj_name )
obj_matrix = APIU.getMatrix( obj, 'worldMatrix' )
obj_mtrans = OpenMaya.MTransformationMatrix( base_matrix )

#new_matrix = OpenMaya.MMatrix()
#new_mtrans = OpenMaya.MTransformationMatrix( new_matrix )
new_mtrans = OpenMaya.MTransformationMatrix( base_matrix )

# Translate first.
mvec = OpenMaya.MVector( x, y, 0.0 )
new_mtrans.addTranslation( mvec, OpenMaya.MSpace.kObject )

# Rotate last.
#util = OpenMaya.MScriptUtil()
#util.createFromDouble( 0.0, 0.0, rot_z )
#ptr = util.asDoublePtr()
#new_mtrans.addRotation( ptr )

rvec = OpenMaya.MVector( 0.0, 0.0, OpenMaya.MAngle( rot_z, OpenMaya.MAngle.kDegrees ).asRadians() )
euler = OpenMaya.MEulerRotation( rvec )
new_mtrans.rotateBy( euler, OpenMaya.MSpace.kObject )

# Apply the new transform.
trans_fn = OpenMaya.MFnTransform( obj )
#trans_fn.setTranslation( new_mtrans.translation( OpenMaya.MSpace.kObject ), OpenMaya.MSpace.kObject )
#trans_fn.setRotation( new_mtrans.rotation() )
trans_fn.set( new_mtrans )
'''

'''
============================
====TEST ANIMATION NAMES====
============================

CHA_ACT_flipTheBird_02
CHA_ACT_pieFreezeEnter_01
CHA_ACT_flipTheBird_01
CHA_ACT_taDaa_01
CHA_ACT_madStart_01
CHA_ACT_talkToTheHand_01
CHA_ACT_yawn_01
CHA_ACT_comeHere_01
CHA_ACT_wrapItUp_01
CHA_ACT_pieSmash_01
CHA_ACT_queenWave_01
CHA_ACT_makeOut_01
CHA_ACT_melloStart_01
CHA_ACT_apathetic_01
CHA_ACT_wave_01
CHA_ACT_nonono_01
CHA_ACT_stop_01
CHA_ACT_pieFreezeExit_01
CHA_ACT_shrug_01
CHA_ACT_basePose
CHA_ACT_happy_01
CHA_ACT_sternEnd_01
CHA_ACT_runAway_01
CHA_ACT_blahBlahBlah_01
CHA_ACT_checkPlease_01
CHA_ACT_meditate_01
CHA_ACT_leanOverGoofy_01
CHA_ACT_soso_01
CHA_ACT_worried_01
CHA_ACT_thumbsDown_01
CHA_ACT_pieArrive_01
CHA_ACT_happyStart_01
CHA_ACT_throwKick_01
CHA_ACT_thumbsUpDub
CHA_ACT_peace_01
CHA_ACT_shameOnYou_01
CHA_ACT_sulking_01
CHA_ACT_madEnd_01
CHA_ACT_pieFreezeHold_01
CHA_ACT_finished_01
CHA_ACT_run_01
CHA_ACT_zombieWalk_01
CHA_ACT_drunk_01
CHA_ACT_giddy_01
CHA_ACT_applause_01
CHA_ACT_itsCold_01
CHA_ACT_hug_01
CHA_ACT_aok_01
CHA_ACT_letsFight_01
CHA_ACT_letsFight_02
CHA_ACT_handPoundFist_01
CHA_ACT_angry_01
CHA_ACT_jumpUpAndDown_01
CHA_ACT_dragRight_01
CHA_ACT_pieGrab_01
CHA_ACT_fistPump_01
CHA_ACT_crazy_01
CHA_ACT_sexGesture_01
CHA_ACT_omg_01
CHA_ACT_hangLoose_01
CHA_ACT_aLittleBit_01
CHA_ACT_loser_01
CHA_ACT_idle_01
CHA_ACT_dragLeft_01
CHA_ACT_retardFace_01
CHA_ACT_jazzHands_01
CHA_ACT_idle_03
CHA_ACT_fingersCrossed_01
CHA_ACT_sternStart_01
CHA_ACT_funny_01
CHA_ACT_waveDouble_01
CHA_ACT_ohNoYouDiDint_01
CHA_ACT_thumbsSideways_01
CHA_ACT_thumbsUp_01
CHA_ACT_drop_01
CHA_ACT_whatDoYouWant_01
CHA_ACT_walk_01
CHA_ACT_throwPunch_01
CHA_ACT_happyEnd_01
CHA_ACT_solidarity_01
CHA_ACT_BA_01
CHA_ACT_wereNumberOne_01
CHA_ACT_melloEnd_01
CHA_ACT_bendAndGrab_01
CHA_ACT_cutNeck_01
CHA_ACT_airQuotes_01
CHA_ACT_pieThrow_01
CHA_ACT_pieHold_01
CHA_ACT_pieLeave_01
CHA_ACT_thumbsDownDub
CHA_ACT_crazyStart
CHA_ACT_throwItem_01
CHA_ACT_kick_01
CHA_ACT_pieDrop_01
CHA_ACT_crazyEnd
CHA_ACT_frightened_01
CHA_ACT_whackOff_02
CHA_ACT_whackOff_01
CHA_ACT_getUp_01
CHA_ACT_idle_02
CHA_ACT_tapFoot_01
CHA_ACT_hang_01
CHA_ACT_angryJump_01
CHA_ACT_snaps fingers_01
CHA_ACT_timeOut_01
'''

    
    
    
    
    
    
    

    
    
    
    
    
    
    
    
    
    
    